#include "visitordata.h"
#include <algorithm>
#include <assert.h>
#include <ctype.h>
#include "parentdata.h"
#include "../code/constantvisitor.h"
#include "../code/codevisitor.h"

bool SemanticVisitorData::isKnown(const std::string & name)
{
	return m_libraryclasses.find(name)!=m_libraryclasses.end()
		|| name=="ARRAY";
};

SemanticVisitorData::SemanticVisitorData(const std::vector<ListNode<ClassNode> *> & root,
										const char * rootclass,
										const char * rootproc)
{
	m_should_print_tables=false;
	m_should_print_code=false;

	m_rootclass=rootclass;
	m_rootproc=rootproc;
	std::transform(m_rootclass.begin(), m_rootclass.end(), m_rootclass.begin(),
                 toupper);
	std::transform(m_rootproc.begin(), m_rootproc.end(), m_rootproc.begin(),
                 toupper);

	createLibraryClasses();
	for(unsigned int i=0;i<root.size();i++)
	{
		const std::vector<ClassNode *> & data=root[i]->vector();
		for (unsigned int j=0;j<data.size();j++)
		{
			bool error=false;
			if (m_classes.find(data[j]->name())!=m_classes.end())
			{
				add(new DataRedefinitionError(data[j],
										   	  m_classes[data[j]->name()]->m_class,
					                          "Class",data[j]->name()
											 ));
				error=true;
			}
			if (isKnown(data[j]->name()) || data[j]->name()=="ANY" || data[j]->name()=="NONE")
			{
				add(new LibraryClassRedefinitionError(data[j],data[j]->name()));
				error=true;
			}
			if (error)
				m_rejected_classes.push_back(new ClassSemantic(data[j]));
			else
				m_classes.insert(std::make_pair<std::string,ClassSemantic *>(data[j]->name(),new ClassSemantic(data[j])));
		}
	}
}

bool less(Error * a, Error * b)
{
	return (*a)<(*b);
}

void SemanticVisitorData::print()
{
	std::sort(m_warnings.begin(),m_warnings.end(),less);
	for (unsigned int i=0;i<m_warnings.size();i++)
		m_warnings[i]->printWithPrelude();
	std::sort(m_errors.begin(),m_errors.end(),less);
	for (unsigned int i=0;i<m_errors.size();i++)
		m_errors[i]->printWithPrelude();

}
SemanticVisitorData::~SemanticVisitorData()
{
	for (unsigned int i=0;i<m_errors.size();i++)
		delete m_errors[i];
	for (unsigned int i=0;i<m_warnings.size();i++)
		delete m_warnings[i];
	for (unsigned  int i=0;i<m_rejected_classes.size();i++)
		 delete m_rejected_classes[i];
	for(std::map<std::string,ClassSemantic *>::iterator it=m_classes.begin();it!=m_classes.end();it++)
		 delete it->second;
	for(std::map<std::string,ClassSemantic *>::iterator it=m_libraryclasses.begin();it!=m_libraryclasses.end();it++)
		 delete it->second;
}

inline bool SemanticVisitorData::exists(const std::string & name)
{
	return this->m_classes.find(name)!=m_classes.end() || isKnown(name);
}

bool SemanticVisitorData::isCorrect(ClassTypeNode * node,bool show_error)
{
  bool exist=exists(node->partialName());
  if (exist && node->partialName()=="ARRAY")
  {
	  if (node->isTAAmount(1))
	  {
		  exist=false;
		  bool texists=exists(node->getTA(0));
		  if (!texists)
			  if (show_error)
				add(new ClassDoesNotExistsError(node,node->getTA(0)));
		  else
		  {
		    if (!show_error)
				exist=true;
			createArrayType(node->getTA(0));
		  }
	  }
	  else
	  {
		  exist=false;
		  if (show_error)
			add(new IncorrectTemplateArgumentCountError(node,"ARRAY",1,node->TAACount()));
	  }
  }
  else
  {
	  if (exist)
	  {
		  if (!(node->isTAAmount(0)))
		  {
		     if (show_error)
				add(new IncorrectTemplateArgumentCountError(node,node->partialName(),0,node->TAACount()));
		     exist=false;
		  }
	  }
	  else
	  {
	    if (show_error)
			add(new ClassDoesNotExistsError(node,node->partialName()));
	  }
  }
  return exist;
}

bool SemanticVisitorData::scanLoop(ClassTypeNode * scanned,
								   std::vector<std::string> & loop)
{
  if (!isCorrect(scanned))
	  return false;
  if (m_classes.find(scanned->partialName())!=m_classes.end())
  {
	 assert(loop.size());
	 //If found loop
	 if (scanned->partialName()==loop[0])
	 {
		 loop.push_back(scanned->partialName());
		 return true;
	 }
	 //Scan parents for loop
	 ClassSemantic * scannedclass=m_classes[scanned->partialName()];
	 bool found=false;
	 for (unsigned int i=0;i<scannedclass->inheritanceCount();i++)
	 {
		 std::vector<std::string> tmp=loop;
		 tmp.push_back(scanned->partialName());
		 found=scanLoop(scannedclass->inherited(i),tmp);
		 if (found)
		 {
			 loop=tmp;
			 return true;
		 }
	 }
	 return false;
  }
  else return false;
}

void SemanticVisitorData::scanForErrors()
{
	scanForInheritanceLoops();
	fillInheritance();
	generateFlatShotForms();
	scanInterfaces();
	addAnyIfNeed();
	checkFMI();
	if (m_loops.size()==0)
		checkDuplicateInheritance();
	checkAndMarkRootClassProcedure();
	fixClients();
	findConstructors();
	fillConverters();
	checkArgumentLists();
	if (!hasErrors())
	{
		//Proceed to performing code tests
		MethodChecker * checker=new MethodChecker();
		this->applyVisitor(checker);
		delete checker;
	}
}


void removeDuplicatesAndSort(std::vector<std::string> & loop)
{
  for (unsigned int i=0;i<loop.size();i++)
  {
	  std::vector<std::string> tmp=loop;
	  std::string test=tmp[i];
	  tmp.erase(tmp.begin()+i);
	  
	  bool found=false;
	  for (unsigned int j=0;(j<tmp.size()) && !found;j++)
		  if (tmp[j]==test)
			  found=true;

	  if (found)
		  loop=tmp;
  }
  std::sort(loop.begin(),loop.end());
}

bool addToLoopVector(std::vector<std::string> & loop,std::vector< std::vector<std::string> > & v)
{
	bool found=false;
	for (unsigned  int i=0;(i<v.size()) && !found;i++)
	{
		if (v[i].size()==loop.size())
		{
			bool ok=true;
			for (unsigned int j=0;j<loop.size();j++)
			{
				if (loop[j]!=v[i][j])
					ok=false;
			}
			found=ok;
		}
	}

	if (!found)
		v.push_back(loop);
	return !found;
}

void SemanticVisitorData::scanForInheritanceLoops()
{
	//Scan for loop
	for (std::map<std::string,ClassSemantic *>::iterator it=m_classes.begin();it!=m_classes.end();it++)
	{
		bool foundloop=false;
		std::vector<std::string> v;
		v.push_back(it->second->cnode()->name());
		for (unsigned int i=0;(i<it->second->inheritanceCount()) && (!foundloop);i++)
		{
			foundloop=scanLoop(it->second->inherited(i),v);
		}
		if (foundloop)
		{
			removeDuplicatesAndSort(v);
			bool notfound=addToLoopVector(v,m_loops);
			if (notfound)
				add( new InheritanceLoopError(it->second->cnode(),v));
		}
	}
	//Fills attributes of data
	for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();it!=m_classes.end();it++)
	{
		it->second->fillAttributes(this);
	}
}

void SemanticVisitorData::printAttrTables()
{
	for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
	{
		printf("class %s{\n",it->first.c_str());
		it->second->printAttrTable();
		printf("}\n");
	}
	for (unsigned int i=0;i<m_rejected_classes.size();i++)
	{
		m_rejected_classes[i]->printAttrTable();
	}
	
}

void SemanticVisitorData::fillInheritance()
{
  for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
  {
	  it->second->fillInheritance(this);
  }
  for (unsigned int i=0;i<m_rejected_classes.size();i++)
  {
	m_rejected_classes[i]->fillInheritance(this);
  }
}


void SemanticVisitorData::generateFlatShotForms()
{
 for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
  {
	  it->second->generateFlatShotForm(this);
  }
  for (unsigned int i=0;i<m_rejected_classes.size();i++)
  {
	m_rejected_classes[i]->generateFlatShotForm(this);
  }
}

bool SemanticVisitorData::getClassByName(const std::string & name,ClassSemantic *& className)
{
  if (m_classes.find(name)!=m_classes.end())
  {
      className=m_classes[name];
	  return true;
  }
  if (m_libraryclasses.find(name)!=m_libraryclasses.end())
  {
	  className=m_libraryclasses[name];
	  return true;
  }
  return false;
}


void SemanticVisitorData::scanInterfaces()
{
 for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
  {
	  it->second->checkInterface(this);
  }
  for (unsigned int i=0;i<m_rejected_classes.size();i++)
  {
	m_rejected_classes[i]->checkInterface(this);
  }
}

void SemanticVisitorData::addAnyIfNeed()
{
	for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
  {
	  it->second->addAnyIfNeed(this);
  }
	for (unsigned int i=0;i<m_rejected_classes.size();i++)
  {
	m_rejected_classes[i]->addAnyIfNeed(this);
  }
}

void SemanticVisitorData::checkFMI()
{
 for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
  {
	  it->second->checkFMI(this);
  }
  for (unsigned int i=0;i<m_rejected_classes.size();i++)
  {
	m_rejected_classes[i]->checkFMI(this);
  }
}


void SemanticVisitorData::fixClients()
{
 for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
  {
	  it->second->fixClients();
  }
  for (unsigned int i=0;i<m_rejected_classes.size();i++)
  {
	m_rejected_classes[i]->fixClients();
  }
}

void SemanticVisitorData::findConstructors()
{
  ParentData data;
  data.Classes=this;
	for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
  {
	  it->second->findConstructors(&data);
  }
  for (unsigned int i=0;i<m_rejected_classes.size();i++)
  {
	m_rejected_classes[i]->findConstructors(&data);
  }
}

void SemanticVisitorData::fillConverters()
{
  ParentData data;
  data.Classes=this;
	for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
  {
	  it->second->fillConverters(&data);
  }
  for (unsigned int i=0;i<m_rejected_classes.size();i++)
  {
	m_rejected_classes[i]->fillConverters(&data);
  }

}

void SemanticVisitorData::checkArgumentLists()
{
 ParentData data;
 data.Classes=this;
  for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
  {
	  it->second->checkFormalArgsAndLocalVars(&data);
  }
  for (unsigned int i=0;i<m_rejected_classes.size();i++)
  {
	m_rejected_classes[i]->checkFormalArgsAndLocalVars(&data);
  }
}


bool SemanticVisitorData::markAsScanned(Compound * compound)
{
	if (m_routines.find(compound)!=m_routines.end() && compound != NULL)
		return false;
	if (compound!=NULL)
		m_routines.insert(compound);
	return true;
}


void SemanticVisitorData::checkDuplicateInheritance()
{
   for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
  {
	  it->second->checkDuplicateInheritance(this);
  }
  for (unsigned int i=0;i<m_rejected_classes.size();i++)
  {
	m_rejected_classes[i]->checkDuplicateInheritance(this);
  }
}

void SemanticVisitorData::applyVisitor(MethodVisitor * visitor)
{
	visitor->data()->Classes=this;
	for (std::map<std::string,ClassSemantic*>::iterator it=m_classes.begin();
		 it!=m_classes.end();
		 it++
		)
    {
	  it->second->applyVisitor(visitor);
    }
}

bool SemanticVisitorData::shouldPrintTables()
{
	return m_should_print_tables;
}

bool SemanticVisitorData::shouldPrintCode()
{
	return m_should_print_code;
}


void SemanticVisitorData::generateConstantTables(bool print)
{
	m_should_print_tables=print;
	ConstantVisitor * checker=new ConstantVisitor();
	this->applyVisitor(checker);
	delete checker;
}

void SemanticVisitorData::generateCode(bool print)
{
	m_should_print_code=print;
	CodeVisitor * checker=new CodeVisitor();
	this->applyVisitor(checker);
	delete checker;
}